#include "scene.h"
#include "AES/qaesencryption.h"
#include <QCryptographicHash>
#include <QStandardPaths>
#include <QFileInfo>
#include <QDir>

uint32_t Scene::win_count = 0;

Scene::Scene(qreal x, qreal y, qreal width, qreal height,QObject *parent)
    : QGraphicsScene(x, y, width, height, parent)
{
    player = new QMediaPlayer();
    QAudioOutput *audioOutput = new QAudioOutput(QMediaDevices::defaultAudioOutput());
    player->setAudioOutput(audioOutput);
    player->setSource(QUrl("qrc:/sounds/Solitaire.mp3"));
    player->setLoops(QMediaPlayer::Infinite);

    password = "qRTQZwY3D1fZ5dFbthPC";
    quint8 iv_16[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
    for (int i=0; i<16; i++)
        iv.append(iv_16[i]);

    table = new CardTable();
    connect(table, &CardTable::gameFinished, this, &Scene::slotGameFinished);

    json userData = loadUserData();
    if (userData != nullptr)
    {
        if (userData.contains("win_count")) win_count = userData["win_count"];
        if (!(userData.contains("play_music") && (!userData["play_music"]))) player->play();
        if (userData.contains("playing_data")) table->initializeByPlayingData(userData["playing_data"]);
    }
    else
    {
        player->play();
    }

    QPixmap foregroundPix(":/textures/foreground.png");
    foregroundPix = foregroundPix.scaled(foregroundPix.size() - QSize(1, 1));
    foreground = new QGraphicsPixmapItem(foregroundPix);
    foreground->setTransformationMode(Qt::SmoothTransformation);
    addItem(foreground);

    tutorial = new QGraphicsPixmapItem(QPixmap(":/textures/tutorial_large.png"));
    addItem(tutorial);
    tutorial->setPos(80, 8);
    tutorial->setZValue(200);
    tutorial->setVisible(false);

    QGraphicsPixmapItem *rightInset = new QGraphicsPixmapItem(QPixmap(":/textures/inset_right.png"));
    rightInset->setTransformationMode(Qt::SmoothTransformation);
    addItem(rightInset);
    rightInset->setPos(965 - 191, 782);

    win_count_label = new WinCountLabel(win_count);
    win_count_label->setPos(21, 830);
    addItem(win_count_label);

    tips = new QGraphicsTextItem("F10 开启/关闭背景音乐");
    tips->setFont(QFont("黑体", 11, QFont::Thin));
    tips->setDefaultTextColor(QColor(255, 128, 0));
    addItem(tips);
    tips->setPos(350, 832);
    tips->setVisible(false);

    exit_button = new BottomButton("退出");
    tutorial_button = new BottomButton("帮助");
    new_game_button = new BottomButton("新游戏");
    addItem(exit_button);
    addItem(tutorial_button);
    addItem(new_game_button);
    exit_button->setPos(973 - 192, 815);
    tutorial_button->setPos(973, 815);
    new_game_button->setPos(1167, 815);

    connect(exit_button, &BottomButton::clicked, this, &Scene::exitClicked);
    connect(tutorial_button, &BottomButton::clicked, this, &Scene::slotTutorialClicked);
    connect(new_game_button, &BottomButton::clicked, this, &Scene::slotStartNewGameClicked);

    addItem(table);
    table->setPos(80, 8);

    move_pressed = false;
    scale_factor = 1.0;
}

Scene::~Scene()
{
    saveUserData();
}

void Scene::slotStartNewGameClicked()
{
    table->startNewGame();
}

void Scene::slotTutorialClicked()
{
    tutorial->setVisible(tutorial->isVisible() ? false : true);
    tips->setVisible(tutorial->isVisible());
    new_game_button->setDisabled(tutorial->isVisible());
}

void Scene::slotGameFinished()
{
    ++win_count;
    win_count_label->setWinCount(win_count);
}

void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mousePressEvent(event);
    if (event->button() != Qt::LeftButton) return;
    QPointF pos = event->scenePos();
    QGraphicsItem *item = itemAt(pos, QTransform());
    if (item != nullptr)
    {
        if (item == foreground)
        {
            move_pressed = true;
            move_begin_point = pos;
            return;
        }
    }
}

void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseReleaseEvent(event);
    if (event->button() != Qt::LeftButton) return;
    if (move_pressed)
    {
        move_pressed = false;
        return;
    }
}

void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsScene::mouseMoveEvent(event);
    if (move_pressed)
    {
        QPointF offset = event->scenePos() - move_begin_point;
        offset = offset * scale_factor;
        emit windowMoved(QPoint(std::round(offset.x()), std::round(offset.y())));
        return;
    }
}

void Scene::wheelEvent(QGraphicsSceneWheelEvent *event)
{
    if (event->modifiers() & Qt::ControlModifier) {
        if (event->delta() > 0) {
            if (scale_factor < 1.2)
                scale_factor += 0.025;
        } else {
            if (scale_factor > 0.8)
                scale_factor -= 0.025;
        }
        emit scaleFactorChanged(scale_factor);
    } else {
        QGraphicsScene::wheelEvent(event);
    }
}

void Scene::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_F10)
    {
        if (player->playbackState() == QMediaPlayer::PlayingState)
        {
            player->pause();
        }
        else
        {
            player->play();
        }
    }
}

json Scene::loadUserData()
{
    QString path = getStoragePath();
    json userData = nullptr;
    if (QFileInfo::exists(path))
    {
        QFile file(path);
        if (file.open(QIODevice::ReadOnly))
        {
            QByteArray passwordHashKey = QCryptographicHash::hash(password.toLocal8Bit(), QCryptographicHash::Sha256);
            QByteArray raw = file.readAll();
            if (raw.size() < 32)
            {
                file.close();
                return nullptr;
            }
            QByteArray encodeText = raw.chopped(32);
            QByteArray hashkey = raw.right(32);
            QByteArray decodeText = QAESEncryption::Decrypt(QAESEncryption::AES_256, QAESEncryption::CBC,
                                                            encodeText, passwordHashKey, iv, QAESEncryption::PKCS7);
            decodeText = QAESEncryption::RemovePadding(decodeText, QAESEncryption::PKCS7);
            if (QCryptographicHash::hash(decodeText, QCryptographicHash::Sha256) == hashkey)
            {
                userData = json::parse(QString::fromLocal8Bit(decodeText).toStdString());
            }
            file.close();
        }
    }
    return userData;
}

void Scene::saveUserData()
{
    json userdata;
    userdata["playing_data"] = table->getPlayingData();
    userdata["play_music"] = (player->playbackState() == QMediaPlayer::PlayingState);
    userdata["win_count"] = win_count;
    QString path = getStoragePath();
    QFile file(path);
    if (file.open(QIODevice::WriteOnly | QIODevice::Truncate))
    {
        QString rawdata = QString::fromStdString(userdata.dump());
        QByteArray passwordHashKey = QCryptographicHash::hash(password.toLocal8Bit(), QCryptographicHash::Sha256);
        QByteArray encodeText = QAESEncryption::Crypt(QAESEncryption::AES_256, QAESEncryption::CBC,
                                                      rawdata.toLocal8Bit(), passwordHashKey, iv, QAESEncryption::PKCS7);
        QByteArray rawdataHashKey = QCryptographicHash::hash(rawdata.toLocal8Bit(), QCryptographicHash::Sha256);
        file.write(encodeText);
        file.write(rawdataHashKey);
        file.close();
    }
}

QString Scene::getStoragePath() const
{
    QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QDir dir(path);
    if (!dir.exists())
    {
        if (!QDir().mkpath(path))
        {
            return "";
        }
    }
    if (!path.endsWith('/'))
        path.append('/');
    path.append("USERDATA");
    return path;
}
